---
title: Functions
sidebar_position: 2
description: Introduction to Mojo `fn` and `def` functions.
---

As mentioned in the [syntax overview](/mojo/manual/basics), Mojo supports two
keywords to declare functions: `def` and `fn`. You can use either declaration
with any function, including the `main()` function, but they have different
default behaviors, as described on this page.

We believe both `def` and `fn` have good use cases and don't consider either to
be better than the other. Deciding which to use is a matter of personal taste as
to which style best fits a given task.

:::note

Functions declared inside a [`struct`](/mojo/manual/structs) are called
"methods," but they have all the same qualities as "functions" described here.

:::

## Anatomy of a function

Both `def` and `fn` function declarations have the same basic components (here
demonstrated with a `def` function):

<pre>
<strong>def</strong> <var>function_name</var><strong>[
&#8203;    </strong><var>parameters ...</var><strong>
](
&#8203;    </strong><var>arguments ...</var><strong>
) -&gt;</strong> <var>return_value_type</var>:
&#8203;    <var>function_body</var>
</pre>

Functions can have:

- Parameters: A function can optionally take one or more compile-time
  _parameter_ values used for metaprogramming.
- Arguments: A function can also optionally take one or more run-time
  _arguments_.
- Return value: A function can optionally return a value.
- Function body: Statements that are executed when you call the function.
  Function definitions must include a body.

All of the optional parts of the function can be omitted, so the minimal
function is something like this:

```mojo
def do_nothing():
    pass
```

If a function takes no parameters, you can omit the square brackets, but the
parentheses are always required.

Although you can't leave out the function body, you can use the `pass` statement
to define a function that does nothing.

### Arguments and parameters

Functions take two kinds of inputs: _arguments_ and _parameters_. Arguments are
familiar from many other languages: they are run-time values passed into the
function.

```mojo
def add(a: Int, b: Int) -> Int:
    return a+b
```

On the other hand, you can think of a parameter as a compile-time variable that
becomes a run-time constant. For example, consider the following function with a
parameter:

```mojo
def add_tensors[rank: Int](a: MyTensor[rank], b: MyTensor[rank]) -> MyTensor[rank]:
    # ...
```

In this case, the `rank` value needs to be specified in a way that can be
determined at compilation time, such as a literal or expression.

When you compile a program that uses this code, the compiler produces a unique
version of the function for each unique `rank` value used in the program, with
`rank` treated as a constant within each specialized version.

This usage of "parameter"
is probably different from what you're used to from other languages, where
"parameter" and "argument" are often used interchangeably. In Mojo, "parameter"
and "parameter expression" refer to compile-time values, and "argument" and
"expression" refer to run-time values.

By default, both arguments and parameters can be specified either by position or
by keyword. These forms can also be mixed in the same function call.

```mojo
# positional
x = add(5, 7)      # Positionally, a=5 and b=7
# keyword
y = add(b=3, a=9)
# mixed
z = add(5, b=7)    # Positionally, a=5
```

For more information on arguments, see [Function arguments](#function-arguments)
on this page. For more information on parameters, see
[Parameterization: compile-time metaprogramming](/mojo/manual/parameters/).

## `def` and `fn` comparison

Defining a function using `def` and `fn` have much in common. They both have the
following requirements:

* You must declare the type of each function parameter and argument.

* If a function doesn't return a value, you can either omit the return type or
  declare `None` as the return type.

  ```mojo
  # The following function definitions are equivalent

  def greet(name: String):
    print("Hello," name)

  def greet(name: String) -> None:
    print("Hello," name)
  ```

* If the function returns a value, you must either declare the return type using
  the <code><strong>-></strong> <var>type</var></code> syntax or provide a
  [named result](#named-results) in the argument list.

  ```mojo
  # The following function definitions are equivalent

  def incr(a: Int) -> Int:
    return a + 1

  def incr(a: Int, out b: Int):
    b = a + 1
  ```

  For more information, see the [Return values](#return-values) section of this
  page.

Where `def` and `fn` differ is error handling.

* The compiler doesn't allow a function declared with `fn` to raise an error
  condition unless it explicitly includes a `raises` declaration. In contrast,
  the compiler assumes that *all* functions declared with `def` *might* raise an
  error. See the [Raising and non-raising
  functions](#raising-and-non-raising-functions) section of this page for more
  information.

As far as a function caller is concerned, there is no difference between
invoking a function declared with `def` vs a function declared with `fn`. You
could reimplement a `def` function as an `fn` function without making any
changes to code that calls the function.

## Function arguments

The rules for arguments described in this section apply to both `def`
and `fn` functions.

:::note Functions with / and * in the argument list

You might see the following characters in
place of arguments: slash (`/`) and/or star (`*`). For example:

```mojo
def myfunc(pos_only, /, pos_or_keyword, *, keyword_only):
```

Arguments **before** the `/` can be passed only by position. Arguments **after**
the `*` can be passed only by keyword. For details, see
[Positional-only and keyword-only arguments](#positional-only-and-keyword-only-arguments)

You may also see argument names prefixed with one or two stars (`*`):

```mojo
def myfunc2(*names, **attributes):
```
An argument name prefixed by a single star character, like `*names` identifies a
[variadic argument](#variadic-arguments), while an argument name prefixed with
a double star, like `**attributes` identifies a
[variadic keyword-only argument](#variadic-keyword-arguments).

:::

### Optional arguments

An optional argument is one that includes a default value, such as the `exp`
argument here:

```mojo
fn my_pow(base: Int, exp: Int = 2) -> Int:
    return base ** exp

fn use_defaults():
    # Uses the default value for `exp`
    var z = my_pow(3)
    print(z)
```

However, you can't define a default value for an argument that's declared with
the [`mut`](/mojo/manual/values/ownership#mutable-arguments-mut) argument
convention.

Any optional arguments must appear after any required arguments. [Keyword-only
arguments](#positional-only-and-keyword-only-arguments), discussed later, can
also be either required or optional.

### Keyword arguments

You can also use keyword arguments when calling a function. Keyword arguments
are specified using the format
<code><var>argument_name</var> = <var>argument_value</var></code>.
You can pass keyword arguments in any order:

```mojo
fn my_pow(base: Int, exp: Int = 2) -> Int:
    return base ** exp

fn use_keywords():
    # Uses keyword argument names (with order reversed)
    var z = my_pow(exp=3, base=2)
    print(z)
```

### Variadic arguments

Variadic arguments let a function accept a variable number of arguments. To
define a function that takes a variadic argument, use the variadic argument
syntax <code>*<var>argument_name</var></code>:

```mojo
fn sum(*values: Int) -> Int:
  var sum: Int = 0
  for value in values:
    sum = sum + value
  return sum
```

The variadic argument `values` here is a placeholder that accepts any number of
passed positional arguments.

You can define zero or more arguments before the variadic argument. When calling
the function, any remaining positional arguments are assigned to the variadic
argument, so any arguments declared **after** the variadic argument can only be
specified by keyword (see
[Positional-only and keyword-only arguments](#positional-only-and-keyword-only-arguments)).

Variadic arguments can be divided into two categories:

* Homogeneous variadic arguments, where all of the passed arguments are the same
  type—all `Int`, or all `String`, for example.
* Heterogeneous variadic arguments, which can accept a set of different argument
  types.

The following sections describe how to work with homogeneous and heterogeneous
variadic arguments.

:::note Variadic parameters

Mojo also supports variadic *parameters*, but with some limitations—for details
see [variadic parameters](/mojo/manual/parameters/#variadic-parameters).

:::

#### Homogeneous variadic arguments

When defining a homogeneous variadic argument (all arguments must be the same
type), use <code>*<var>argument_name</var>: <var>argument_type</var></code>:

```mojo
def greet(*names: String):
    ...
```

Inside the function body, the variadic argument is available as an iterable list
for ease of use. Currently there are some differences in handling the list
depending on whether the arguments are register-passable types (such as `Int`)
or memory-only types (such as `String`).

:::note TODO

We hope to remove these differences in the future.

:::

Register-passable types, such as `Int`, are available as a
[`VariadicList`](/mojo/stdlib/builtin/variadics/VariadicList) type. As
shown in the previous example, you can iterate over the values using a `for..in`
loop.

```mojo
fn sum(*values: Int) -> Int:
  var sum: Int = 0
  for value in values:
    sum = sum+value
  return sum
```

Memory-only types, such as `String`, are available as a
[`VariadicListMem`](/mojo/stdlib/builtin/variadics/VariadicListMem).
Iterating over this list directly with a `for..in` loop currently produces a
reference to the element, which can be mutable with a `mut` variadic list. Use
the `ref` binding pattern to capture a mutable reference if you
want to mutate the elements of the list:

```mojo
def make_worldly(mut *strs: String):
    for ref i in strs:
        i += " world"
```

You can also directly index the list with integers as well:

```mojo
fn make_worldly(mut *strs: String):
    for i in range(len(strs)):
        strs[i] += " world"
```

#### Heterogeneous variadic arguments

Implementing heterogeneous variadic arguments (each argument type may be
different) is somewhat more complicated than homogeneous variadic arguments. To
handle multiple argument types, the function must be
[generic](/mojo/manual/functions/#variadic-arguments), which requires using
[traits](/mojo/manual/traits) and [parameters](/mojo/manual/parameters/). So
the syntax may look a little unfamiliar if you haven't worked with those
features.

The signature for a function with a heterogeneous variadic argument looks like
this:

```mojo
def count_many_things[*ArgTypes: Intable](*args: *ArgTypes):
    ...
```

The parameter list, `[*ArgTypes: Intable]` specifies that the function takes an
`ArgTypes` parameter, which is a list of types, all of which conform to the
[`Intable`](/mojo/stdlib/builtin/int/Intable) trait. The asterisk in `*ArgTypes`
indicates that `ArgTypes` is a **variadic type parameter** (a list of types).

The argument list, `(*args: *ArgTypes)` has the familiar `*args` for the
variadic argument, but instead of a single type, its type is defined as the
variadic type list `*ArgTypes`. The asterisk in `*args` indicates a
**variadic argument**, and the asterisk in `*ArgTypes` refers to the
variadic type parameter.

This means that each argument in `args` has a corresponding type in `ArgTypes`,
so <code>args[<var>n</var>]</code> is of type <code>ArgTypes[<var>n</var>]</code>.

Inside the function, `args` becomes a
[`VariadicPack`](/mojo/stdlib/builtin/variadics/VariadicPack) because the
syntax `*args: *ArgTypes` creates a heterogeneous variadic argument. That means
each element in `args` can be a different type that requires a different amount
of memory. To iterate through the `VariadicPack`, the compiler must know each
element's type (its memory size), so you must use a [parametric `for`
loop](/mojo/manual/decorators/parameter/#parametric-for-statement):

```mojo
fn count_many_things[*ArgTypes: Intable](*args: *ArgTypes) -> Int:
    var total = 0

    @parameter
    for i in range(args.__len__()):
        total += Int(args[i])

    return total

def main():
    print(count_many_things(5, 11.7, 12))
```

```output
28
```

Notice that when calling `count_many_things()`, you don't actually pass in
a list of argument types. You only need to pass in the arguments, and Mojo
generates the `ArgTypes` list itself.


#### Variadic keyword arguments

Mojo functions also support variadic keyword arguments (`**kwargs`). Variadic
keyword arguments allow the user to pass an arbitrary number of keyword
arguments. To define a function that takes a variadic keyword argument, use the
variadic keyword argument syntax <code>**<var>kw_argument_name</var></code>:

```mojo
fn print_nicely(**kwargs: Int) raises:
  for key in kwargs.keys():
      print(key, "=", kwargs[key])

 # prints:
 # `a = 7`
 # `y = 8`
print_nicely(a=7, y=8)
```

In this example, the argument name `kwargs` is a placeholder that accepts any
number of keyword arguments. Inside the body of the function, you can access
the arguments as a dictionary of keywords and argument values (specifically,
an instance of
[`OwnedKwargsDict`](/mojo/stdlib/collections/dict/OwnedKwargsDict)).

There are currently a few limitations:

* Variadic keyword arguments are always implicitly treated as if they
  were declared with the `owned` [argument
  convention](/mojo/manual/values/ownership#argument-conventions), and
  can't be declared otherwise:

  ```mojo
  # Not supported yet.
  fn read_var_kwargs(read **kwargs: Int): ...
  ```

* All the variadic keyword arguments must have the same type, and this
  determines the type of the argument dictionary. For example, if the argument
  is `**kwargs: Float64` then the argument dictionary will be a
  `OwnedKwargsDict[Float64]`.

* The argument type must conform to both the
  [`Movable`](/mojo/stdlib/builtin/value/Movable) and
  [`Copyable`](/mojo/stdlib/builtin/value/Copyable) traits.

* Dictionary unpacking is not supported yet:

  ```mojo
  fn takes_dict(d: Dict[String, Int]):
    print_nicely(**d)  # Not supported yet.
  ```

* Variadic keyword *parameters* are not supported yet:

  ```mojo
  # Not supported yet.
  fn var_kwparams[**kwparams: Int](): ...
  ```

### Positional-only and keyword-only arguments

When defining a function, you can restrict some arguments so that they can
be passed only as positional arguments, or they can be passed only as keyword
arguments.

To define positional-only arguments, add a slash character (`/`) to the
argument list. Any arguments before the `/` are positional-only: they can't be
passed as keyword arguments. For example:

```mojo
fn min(a: Int, b: Int, /) -> Int:
    return a if a < b else b
```

This `min()` function can be called with `min(1, 2)` but can't be called using
keywords, like `min(a=1, b=2)`.

There are several reasons you might want to write a function with
positional-only arguments:

* The argument names aren't meaningful for the caller.
* You want the freedom to change the argument names later on without breaking
  backward compatibility.

For example, in the `min()` function, the argument names don't add any real
information, and there's no reason to specify arguments by keyword.

For more information on positional-only arguments, see [PEP 570 – Python
Positional-Only Parameters](https://peps.python.org/pep-0570/).

Keyword-only arguments are the inverse of positional-only arguments: they can
be specified only by keyword. If a function accepts variadic arguments, any
arguments defined *after* the variadic arguments are treated as keyword-only.
For example:

```mojo
fn sort(*values: Float64, ascending: Bool = True): ...
```

In this example, the user can pass any number of `Float64` values, optionally
followed by the keyword `ascending` argument:

```mojo
var a = sort(1.1, 6.5, 4.3, ascending=False)
```

If the function doesn't accept variadic arguments, you can add a single star
(`*`) to the argument list to separate the keyword-only arguments:

```mojo
fn kw_only_args(a1: Int, a2: Int, *, double: Bool) -> Int:
    var product = a1 * a2
    if double:
        return product * 2
    else:
        return product
```

Keyword-only arguments often have default values, but this is not required. If a
keyword-only argument doesn't have a default value, it is a *required
keyword-only argument*. It must be specified, and it must be specified by
keyword.

Any required keyword-only arguments must appear in the signature before
any optional keyword-only arguments. That is, arguments appear in the following
sequence a function signature:

* Required positional arguments.
* Optional positional arguments.
* Variadic arguments.
* Required keyword-only arguments.
* Optional keyword-only arguments.
* Variadic keyword arguments.

For more information on keyword-only arguments, see [PEP 3102 – Keyword-Only
Arguments](https://peps.python.org/pep-3102/).

## Overloaded functions

All function declarations must specify argument types, so if you want a
function to work with different data types, you need to implement
separate versions of the function that each specify different argument types.
This is called "overloading" a function.

For example, here's an overloaded `add()` function that can accept either
`Int` or `String` types:

```mojo
fn add(x: Int, y: Int) -> Int:
    return x + y

fn add(x: String, y: String) -> String:
    return x + y
```

If you pass anything other than `Int` or `String` to the `add()` function,
you'll get a compiler error. That is, unless `Int` or `String` can implicitly
cast the type into their own type. For example, `String` includes an overloaded
version of its constructor (`__init__()`) that supports
[implicit conversion](/mojo/manual/lifecycle/life#constructors-and-implicit-conversion)
from a `StringLiteral` value. Thus, you can also pass a `StringLiteral` to a
function that expects a `String`.

When resolving an overloaded function call, the Mojo compiler tries each
candidate function and uses the one that works (if only one version works), or
it picks the closest match (if it can determine a close match), or it reports
that the call is ambiguous (if it can't figure out which one to pick). For
details on how Mojo picks the best candidate, see
[Overload resolution](#overload-resolution).

If the compiler can't figure out which function to use, you can resolve the
ambiguity by explicitly casting your value to a supported argument type. For
example, the following code calls the overloaded `foo()` function,
but both implementations accept an argument that supports [implicit
conversion](/mojo/manual/lifecycle/life#constructors-and-implicit-conversion)
from `StringLiteral`. So, the call to `foo(string)` is ambiguous and creates a
compiler error. You can fix this by casting the value to the type you really
want:

```mojo
struct MyString:
    @implicit
    fn __init__(out self, string: StringLiteral):
        pass

fn foo(name: String):
    print("String")

fn foo(name: MyString):
    print("MyString")

fn call_foo():
    alias string = "Hello"
    # foo(string) # error: ambiguous call to 'foo' ... This call is ambiguous because two `foo` functions match it
    foo(MyString(string))
```

Overloading also works with combinations of both `fn` and `def` function
declarations.

### Overload resolution

When resolving an overloaded function, Mojo does not consider the return type
or other contextual information at the call site—it considers only parameter and
argument types and whether the functions are instance methods or static methods.

The overload resolution logic filters for candidates according to the following
rules, in order of precedence:

1. Candidates requiring the smallest number of implicit conversions (in both
   arguments and parameters).
2. Candidates without variadic arguments.
3. Candidates without variadic parameters.
4. Candidates with the shortest parameter signature.
5. Non-`@staticmethod` candidates (over `@staticmethod` ones, if available).

If there is more than one candidate after applying these rules, the overload
resolution fails. For example:

```mojo
@register_passable("trivial")
struct MyInt:
    """A type that is implicitly convertible to `Int`."""
    var value: Int

    @implicit
    fn __init__(out self, _a: Int):
        self.value = _a

fn foo[x: MyInt, a: Int]():
    print("foo[x: MyInt, a: Int]()")

fn foo[x: MyInt, y: MyInt]():
    print("foo[x: MyInt, y: MyInt]()")

fn bar[a: Int](b: Int):
    print("bar[a: Int](b: Int)")

fn bar[a: Int](*b: Int):
    print("bar[a: Int](*b: Int)")

fn bar[*a: Int](b: Int):
    print("bar[*a: Int](b: Int)")

fn parameter_overloads[a: Int, b: Int, x: MyInt]():
    # `foo[x: MyInt, a: Int]()` is called because it requires no implicit
    # conversions, whereas `foo[x: MyInt, y: MyInt]()` requires one.
    foo[x, a]()

    # `bar[a: Int](b: Int)` is called because it does not have variadic
    # arguments or parameters.
    bar[a](b)

    # `bar[*a: Int](b: Int)` is called because it has variadic parameters.
    bar[a, a, a](b)

parameter_overloads[1, 2, MyInt(3)]()

struct MyStruct:
    fn __init__(out self):
        pass

    fn foo(mut self):
        print("calling instance method")

    @staticmethod
    fn foo():
        print("calling static method")

fn test_static_overload():
    var a = MyStruct()
    # `foo(mut self)` takes precedence over a static method.
    a.foo()
```

```output
foo[x: MyInt, a: Int]()
bar[a: Int](b: Int)
bar[*a: Int](b: Int)
```

## Return values

Return value types are declared in the signature using the
<code><strong>-></strong> <var>type</var></code> syntax. Values are
passed using the `return` keyword, which ends the function and returns the
identified value (if any) to the caller.

```mojo
def get_greeting() -> String:
    return "Hello"
```

By default, the value is returned to the caller as an owned value. As with
arguments, a return value may be [implicitly
converted](/mojo/manual/lifecycle/life#constructors-and-implicit-conversion) to
the named return type. For example, the previous example calls `return` with a
string literal, `"Hello"`, which is implicitly converted to a `String`.

:::note Returning a reference

A function can also return a mutable or immutable reference using a `ref` return
value. For details, see
[Lifetimes, origins, and references](/mojo/manual/values/lifetimes).

:::

### Named results

Named function results allow a function to return a value that can't be moved or
copied. Named result syntax lets you specify a named, uninitialized variable to
return to the caller using the `out` argument convention:

```mojo
def get_name_tag(var name: String, out name_tag: NameTag):
    name_tag = NameTag(name^)
```

The `out` argument convention identifies an uninitialized variable that the
function must initialize. (This is the same as the `out` convention used in
[struct constructors](/mojo/manual/lifecycle/life#constructor).) The `out`
argument for a named result can appear anywhere in the argument list, but by
convention, it should be the last argument in the list.

A function can declare only one return value, whether it's declared using an
`out` argument or using the standard <code><strong>-></strong>
<var>type</var></code> syntax.

A function with a named result argument doesn't need to include an explicit
`return` statement, as shown above. If the function terminates without a `return`,
or at a `return` statement with no value, the value of the `out` argument is
returned to the caller. If it includes a `return` statement with a value, that
value is returned to the caller, as usual.

The fact that a function uses a named result is transparent to the caller. That
is, these two signatures are interchangeable to the caller:

```mojo
def get_name_tag(var name: String) -> NameTag:
    ...
def get_name_tag(var name: String, out name_tag: NameTag):
    ...
```

In both cases, the call looks like this:

```mojo
tag = get_name_tag("Judith")
```

Because the return value is assigned to this special `out` variable, it doesn't
need to be moved or copied when it's returned to the caller. This means that you
can create a function that returns a type that can't be moved or copied, and
which takes several steps to initialize:

```mojo
struct ImmovableObject:
    var name: String

    fn __init__(out self, var name: String):
        self.name = name^

def create_immovable_object(var name: String, out obj: ImmovableObject):
    obj = ImmovableObject(name^)
    obj.name += "!"
    # obj is implicitly returned

def main():
    my_obj = create_immovable_object("Blob")
```

By contrast, the following function with a standard return value doesn't work:

```mojo
def create_immovable_object2(var name: String) -> ImmovableObject:
    obj = ImmovableObject(name^)
    obj.name += "!"
    return obj^ # Error: ImmovableObject is not copyable or movable
```

Because `create_immovable_object2` uses a local variable to store the object
while it's under construction, the return call requires it to be either moved
or copied to the callee. This isn't an issue if the newly-created value is
returned immediately:

```mojo
def create_immovable_object3(var name: String) -> ImmovableObject:
    return ImmovableObject(name^) # OK
```

## Raising and non-raising functions

By default, when a function raises an error, the function terminates immediately
and the error propagates to the calling function. If the calling function
doesn't handle the error, it continues to propagate up the call stack.

```mojo
def raises_error():
    raise Error("There was an error.")
```

The Mojo compiler *always* treats a function declared with `def` as a *raising
function*, even if the body of the function doesn't contain any code that could
raise an error.

Functions declared with `fn` without the `raises` keyword are *non-raising
functions*—that is, they are not allowed to propagate an error to the calling
function. If a non-raising function calls a raising function, it **must handle
any possible errors.**

```mojo
# This function will not compile
fn unhandled_error():
    raises_error()   # Error: can't call raising function in a non-raising context

# Explicitly handle the error
fn handle_error():
    try:
        raises_error()
    except e:
        print("Handled an error:", e)

# Explicitly propagate the error
fn propagate_error() raises:
    raises_error()

```

If you're writing code that you expect to use widely or distribute as a package,
you may want to use `fn` functions for APIs that don't raise errors to limit
the number of places users need to add unnecessary error handling code. For some
extremely performance-sensitive code, it may be preferable to avoid run-time
error-handling.

For more information, see
[Errors, error handling, and context managers](/mojo/manual/errors).
